import type { Pool } from 'workerpool'
import workerpool from 'workerpool'

interface IOptions {
  /**
   * worker 文件相对路径
   * @example "new URL('./runWithDepts.ts?worker_file', import.meta.url).href"
   */
  workerPath: string
  /**
   * worker 方法名称
   * @description 与传入 `workerpool.worker({ workerName })` 参数相同
   * @example "runWithDepts"
   */
  workerName: string
}

export class AppWorker {
  /**
   * 最大线程数
   * @description 与 CPU 有关
   */
  MAX_WORKER = 8
  /**
   * 线程池
   */
  pool: Pool | undefined
  /**
   * worker 文件路径
   */
  workerPath: string
  /**
   * worker 方法名称
   */
  workerName: string

  constructor(options: IOptions) {
    this.workerPath = options.workerPath
    this.workerName = options.workerName
  }

  /**
   * 静态线程池
   * @description 用来执行一些简单任务
   */
  static get pool() {
    return workerpool.pool()
  }

  /**
   * 执行方法
   * @param fn 方法函数
   * @param args 方法参数
   * @returns 执行结果
   */
  run<T extends any[], R>(
    fn: (depts: any, ...args: T) => Promise<R> | R,
    ...args: T
  ) {
    if (!this.pool) {
      this.patchWorker()
      this.pool = workerpool.pool(this.workerPath, {
        maxWorkers: this.MAX_WORKER
      })
    }

    return this.pool.exec(this.workerName, [String(fn)].concat(args))
  }

  private patchWorker() {
    if (globalThis.Worker && !(globalThis.Worker as any).patched) {
      class PatchedWorker extends Worker {
        static patched = true
        constructor(scriptURL: string | URL, options?: WorkerOptions) {
          super(scriptURL, Object.assign({}, options, { type: 'module' }))
        }
      }
      globalThis.Worker = PatchedWorker
    }
  }
}
